#pragma once
#include "base/embedded/clock/ClockSource.h"
#include "base/unit/MHz.h"
#include "clock_source_handle.h"
#include "hal.h"
#include <cstdint>
#include <string>

namespace bsp
{
	class PllClockSource :
		public base::clock::clock_source_handle
	{
	private:
		/* #region ChannelFactorConfig */

		class ChannelFactorConfig
		{
		private:
			uint32_t _m{};
			uint32_t _n{};
			uint32_t _p{};
			uint32_t _q{};

		public:
			ChannelFactorConfig(std::map<std::string, uint32_t> const &channel_factor_map)
			{
				{
					auto it = channel_factor_map.find("m");
					if (it != channel_factor_map.end())
					{
						_m = it->second;
					}
				}

				{
					auto it = channel_factor_map.find("n");
					if (it != channel_factor_map.end())
					{
						_n = it->second;
					}
				}

				{
					auto it = channel_factor_map.find("p");
					if (it != channel_factor_map.end())
					{
						_p = it->second;
					}
				}

				{
					auto it = channel_factor_map.find("q");
					if (it != channel_factor_map.end())
					{
						_q = it->second;
					}
				}
			}

			uint32_t M() const
			{
				return _m;
			}

			uint32_t N() const
			{
				return _n;
			}

			uint32_t P() const
			{
				return _p;
			}

			uint32_t Q() const
			{
				return _q;
			}
		};

		/* #endregion */

		inline static bool _opened = false;
		inline static base::unit::MHz _p_freq;
		inline static base::unit::MHz _q_freq;

		/* #region 配置帮助函数 */

		constexpr uint32_t get_pll_source_define(std::string input_channel_name)
		{
			if (input_channel_name == "hse")
			{
				return RCC_PLLSOURCE_HSE;
			}
			else if (input_channel_name == "hsi")
			{
				return RCC_PLLSOURCE_HSI;
			}
			else
			{
				throw std::invalid_argument{"不支持该输入通道"};
			}
		}

		base::unit::MHz get_input_frequency(std::string const &input_channel_name)
		{
			if (input_channel_name == "hse")
			{
				base::clock::ClockSource hse{"hse"};
				return hse.Frequency();
			}
			else if (input_channel_name == "hsi")
			{
				throw std::invalid_argument{"不支持该输入通道"};
			}
			else if (input_channel_name == "csi")
			{
				throw std::invalid_argument{"不支持该输入通道"};
			}
			else
			{
				throw std::invalid_argument{"不支持该输入通道"};
			}
		}

		/* #endregion */

	public:
		virtual base::unit::MHz Frequency(std::string const &output_channel_name) const override
		{
			if (!_opened)
			{
				throw std::runtime_error{"pll 还未打开，无法查看频率"};
			}

			if (output_channel_name == "p")
			{
				return _p_freq;
			}
			else if (output_channel_name == "q")
			{
				return _q_freq;
			}
			else
			{
				throw std::invalid_argument{"没有该输出通道"};
			}
		}

		virtual void Configure(std::string const &input_channel_name,
							   std::map<std::string, uint32_t> const &channel_factor_map) override;

		virtual void TurnOff() override
		{
			RCC_OscInitTypeDef def{};
			def.OscillatorType = RCC_OSCILLATORTYPE_NONE;
			def.PLL.PLLState = RCC_PLL_OFF;
			HAL_StatusTypeDef result = HAL_RCC_OscConfig(&def);
			if (result != HAL_StatusTypeDef::HAL_OK)
			{
				throw std::runtime_error{"关闭 PLL 失败。"};
			}

			_opened = false;
		}
	};
} // namespace bsp
